/*************************************************************************************
 *
 *	模块名称 : FrameBuffer函数接口模块
 *	文件名称 : FrameBuffer.c
 *	版   本 : V1.0
 *	说   明 : 根据显示的需求将图形打印到屏幕上。
 *	修改记录 :
 *		     版本号       日期         作者        说明
 *	         V1.0     2023-04-10      rush_long        发布
 *
 *	
 *
 *************************************************************************************/

#include "02_FrameBuffer.h"
#include "02_font.h"
#include "02_ziku.h"
#include <sys/mman.h>

FB_T *FrameBuffInit(const char *pDevName)
{
	int fb = 0;
	int ret = 0;
	FB_T *pFbInfo = NULL;

	pFbInfo = malloc(sizeof(FB_T));           //申请存储设备信息结构体的空间 
	if (NULL == pFbInfo)
	{
		perror("create fb info struct failed");
		return NULL;
	}

	fb = open(pDevName, O_RDWR);			//打开设备文件
	if (-1 == fb)
	{
		perror("fail to open");
		return NULL;
	}

	ret = ioctl(fb, FBIOGET_VSCREENINFO, &pFbInfo->ScreenInfo);			//获取设备信息
	if (-1 == ret)
	{
		perror("get screen info failed");
		return NULL;
	}

	printf("=================== Lcd Info ====================\n");
	printf("Width: %d\n", pFbInfo->ScreenInfo.xres_virtual);
	printf("Height: %d\n", pFbInfo->ScreenInfo.yres_virtual);

	if (IS_RGB565_FORMAT(pFbInfo->ScreenInfo.bits_per_pixel))		//判断设备RGB是565标准还是888标准
	{
		printf("FrameBuff Device Rgb565 Format!\n");
	}
	else if (IS_RGB888_FORMAT(pFbInfo->ScreenInfo.bits_per_pixel))
	{
		printf("FrameBuff Device Rgb888 Format!\n");
	}

	printf("================================================\n");

	if (IS_RGB565_FORMAT(pFbInfo->ScreenInfo.bits_per_pixel))       //根据RGB标准不同去申请相应的显存空间
	{
		pFbInfo->pRGB565Addr = mmap(NULL, pFbInfo->ScreenInfo.xres_virtual * pFbInfo->ScreenInfo.yres_virtual * pFbInfo->ScreenInfo.bits_per_pixel / 8,
	                                    PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
	}
	else if (IS_RGB888_FORMAT(pFbInfo->ScreenInfo.bits_per_pixel))
	{
		pFbInfo->pRGB888Addr = mmap(NULL, pFbInfo->ScreenInfo.xres_virtual * pFbInfo->ScreenInfo.yres_virtual * pFbInfo->ScreenInfo.bits_per_pixel / 8,
	                                    PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
	}

	printf("create Frmamebuff size:%d display buff success!\n", pFbInfo->ScreenInfo.xres_virtual * pFbInfo->ScreenInfo.yres_virtual * pFbInfo->ScreenInfo.bits_per_pixel / 8);

	return pFbInfo;
}

int FrameBuffDeInit(FB_T *pFb)
{
	if (IS_RGB565_FORMAT(pFb->ScreenInfo.bits_per_pixel))				//根据RGB的标准来销毁空间
	{
		munmap(pFb->pRGB565Addr, pFb->ScreenInfo.xres_virtual * pFb->ScreenInfo.yres_virtual * pFb->ScreenInfo.bits_per_pixel / 8);
	}
	else if (IS_RGB888_FORMAT(pFb->ScreenInfo.bits_per_pixel))
	{
		munmap(pFb->pRGB888Addr, pFb->ScreenInfo.xres_virtual * pFb->ScreenInfo.yres_virtual * pFb->ScreenInfo.bits_per_pixel / 8);
	}

	free(pFb);				//释放Init函数中所申请的空间

	return 0;
}

int DrawOnePixel(FB_T *pFb, int x, int y, unsigned char TmpR, unsigned char TmpG, unsigned char TmpB)
{
	RGB888_T *pTmp1 = NULL;
	RGB565_T *pTmp2 = NULL;

	if (IS_RGB888_FORMAT(pFb->ScreenInfo.bits_per_pixel))			//判断RGB标准并作出相应的绘点操作
	{
		pTmp1 = pFb->pRGB888Addr;
		pTmp1 += y * (pFb->ScreenInfo.xres_virtual) + x;
		FILL_RGB888_COL(pTmp1, TmpR, TmpG, TmpB);
	}
	else if (IS_RGB565_FORMAT(pFb->ScreenInfo.bits_per_pixel))
	{
		pTmp2 = pFb->pRGB565Addr;
		pTmp2 += y * (pFb->ScreenInfo.xres_virtual) + x;
		FILL_RGB565_COL(pTmp2, TmpR, TmpG, TmpB);
	}

	return 0;
}

int DrawHorLine(FB_T *pFb, int x, int y, int width, unsigned char TmpR, unsigned char TmpG, unsigned char TmpB)
{
	int i = 0;

	for (i = 0; i < width; i++)			//利用绘点函数和循环将x坐标进行变换绘点
	{
		DrawOnePixel(pFb, x + i, y, TmpR, TmpG, TmpB);
	}

	return 0;
}

int DrawVerLine(FB_T *pFb, int x, int y, int height, unsigned char TmpR, unsigned char TmpG, unsigned char TmpB)
{
	int i = 0;

	for (i = 0; i < height; i++)		//利用绘点函数和循环将y坐标进行变换绘点
	{
		DrawOnePixel(pFb, x, y + i, TmpR, TmpG, TmpB);
	}

	return 0;
}

int DrawHollowRect(FB_T *pFb, int x, int y, int width, int height, unsigned char TmpR, unsigned char TmpG, unsigned char TmpB)
{
	DrawHorLine(pFb, x, y, width, TmpR, TmpG, TmpB);		//分别绘制出两条横线和两条竖线
	DrawHorLine(pFb, x, y + height, width, TmpR, TmpG, TmpB);
	DrawVerLine(pFb, x, y, height, TmpR, TmpG, TmpB);
	DrawVerLine(pFb, x + width, y, height, TmpR, TmpG, TmpB);

	return 0;
}

int DrawSolidRect(FB_T *pFb, int x, int y, int width, int height, unsigned char TmpR, unsigned char TmpG, unsigned char TmpB)
{
	int i = 0;

	for (i = 0; i < height; i++)				//循环绘制横线
	{
		DrawHorLine(pFb, x, y + i, width, TmpR, TmpG, TmpB);
	}

	return 0;
}

int ClearScreen(FB_T *pFb)
{
	DrawSolidRect(pFb, 0, 0, pFb->ScreenInfo.xres_virtual, pFb->ScreenInfo.yres_virtual, 0, 0, 0);			//调用一次绘实心矩形
	
	return 0;
}

int DrawOneAscii(FB_T *pFb, int x, int y, unsigned char ascii, unsigned char TmpR, unsigned char TmpG, unsigned char TmpB)
{	
	unsigned char zimo[16] = {0};
	int i = 0;
	int j = 0;
	unsigned char flag = 0x80;

	memcpy(zimo, &fontdata_8x16[ascii * 16], 16);					//将字模文件中的信息拷贝到我们定义的变量中

	for (j = 0; j < 16; j++)										//利用字模和嵌套循环绘制出字符
	{
		for (flag = 0x80, i = 0; i < 8; i++, flag >>= 1)
		{
			if (zimo[j] & flag)
			{
				DrawOnePixel(pFb, x + i, y + j, TmpR, TmpG, TmpB);
			}
		}
	}

	return 0;
}

int DrawString(FB_T *pFb, int x, int y,  const char *pstr, unsigned char TmpR, unsigned char TmpG, unsigned char TmpB)
{
	const char *ptmp = NULL;
	int i = 0;

	ptmp = pstr;
	while (*ptmp != '\0')					//循环到指针指向'\0'为止，停止显示字母
	{
		DrawOneAscii(pFb, x + 6 * i, y, *ptmp, TmpR, TmpG, TmpB);			//每次显示的位置需要算上上一个字母所占的位置还有字符间的间距
		i++;
		ptmp++;
	}

	return 0;
}

int DrawOneHanZi(FB_T *pFb, int x, int y,  const char *pstr, unsigned char TmpR, unsigned char TmpG, unsigned char TmpB)
{
	unsigned char zimo[32] = {0};
	int i = 0;
	int j = 0;
	int IsExist = 0;
	unsigned char flag = 0x80;
	int n = 0;

	for (n = 0; n < sizeof(gHanzi) / sizeof(gHanzi[0]); n++)		//判断字库里是否存在这个字的字模
	{
		if (!strcmp(pstr, gHanzi[n].str))
		{
			memcpy(zimo, gHanzi[n].zimo, sizeof(zimo));
			IsExist = 1;
		}
	}

	if (!IsExist)
	{
		return 0;
	}

	for (j = 0; j < 16; j++)				//分成左右两部分显示整个汉字
	{
		for (flag = 0x80, i = 0; i < 8; i++, flag >>= 1)				//左半边汉字显示
		{
			if (flag & zimo[2*j])
			{
				DrawOnePixel(pFb, x + i, y + j, TmpR, TmpG, TmpB);
			}
		}
		for (flag = 0x80, i = 8; i < 16; i++, flag >>= 1)				//右半边汉字显示
		{
			if (flag & zimo[2*j+1])
			{
				DrawOnePixel(pFb, x + i, y + j, TmpR, TmpG, TmpB);
			}
		}
	}
	
	return 0;
}

int DrawHanZiStr(FB_T *pFb, int x, int y,  const char *pstr, unsigned char TmpR, unsigned char TmpG, unsigned char TmpB)
{
	char Hanzi[4] = {0};
	const char *ptmp = NULL;
	int i = 0;
	
	ptmp = pstr;
	while (*ptmp != '\0')			//循环到指针指向为'\0'为止
	{
		memcpy(Hanzi, ptmp, 3);		//将需要显示的汉字拷贝进我们定义的汉字变量中
		DrawOneHanZi(pFb, x + 20 * i, y, Hanzi, TmpR, TmpG, TmpB);			//调用显示汉字函数显示汉字
		i++;
		ptmp += 3;
	}

	return 0;
}

int DrawLine(FB_T *pFb, int x0, int y0, int x1, int y1, unsigned char TmpR, unsigned char TmpG, unsigned char TmpB)
{
	int TmpX = 0;
	int TmpY = 0;
	double k = 0;
	int i = 0;

	if (y0 == y1)						//讨论横线部分
	{
		if (x0 < x1)
		{
			DrawHorLine(pFb, x0, y0, x1 - x0, TmpR, TmpG, TmpB);
			return 0;
		}
		else if (x0 > x1)
		{
			DrawHorLine(pFb, x1, y1, x0 - x1, TmpR, TmpG, TmpB);
			return 0;
		}
	}

	if (x0 == x1)						//讨论竖线部分
	{
		if (y0 < y1)
		{
			DrawVerLine(pFb, x0, y0, y1 - y0, TmpR, TmpG, TmpB);
			return 0;
		}
		else if (y0 > y1)
		{
			DrawVerLine(pFb, x1, y1, y0 - y1, TmpR, TmpG, TmpB);
			return 0;
		}
	}

	k = (double)(y1 - y0) / (double)(x1 - x0);						//计算斜率
	if (k > 0 && k <= 1 || k < 0 && k >=-1)							//判断斜率
	{
		if (x0 < x1)					//判断哪个点在左边
		{
			for (i = x0; i <= x1; i++)
			{
				DrawOnePixel(pFb, i, y0 + k * (i - x0), TmpR, TmpG, TmpB);					//y + k * (i - x0)就是根据斜率算出的y坐标
			}
		}
		else if (x0 > x1)
		{
			for (i = x1; i <= x0; i++)
			{
				DrawOnePixel(pFb, i, y1 + k * (i - x1), TmpR, TmpG, TmpB);
			}
		}		
	}
	else if (k > 1 || k < -1)										//判断斜率
	{
		if (y0 < y1)
		{
			for (i = y0; i <= y1; i++)							//判断哪个点在上方
			{
				DrawOnePixel(pFb, x0 + ((i-y0) / k), i, TmpR, TmpG, TmpB);						//x + ((i - y) / k)就是根据斜率计算出的x坐标
			}
		}
		else if (y0 > y1)
		{
			for (i = y1; i <= y0; i++)
			{
				DrawOnePixel(pFb, x1 + ((i-y1) / k), i, TmpR, TmpG, TmpB);
			}
		}
	}

	return 0;
}

int DrawBmp(FB_T *pFb, int x, int y, int width, int height, const char *pBmpName)
{
	int fd = 0;
	unsigned char tmpbuf[3] = {0};
	RGB888_T TmpCol;
	int i = 0;
	int j = 0;

	fd = open(pBmpName, O_RDONLY);						//打开图片文件
	if (-1 == fd)
	{
		fprintf(stderr, "open %s Bmp failed!\n", pBmpName);
		return -1;
	}

	lseek(fd, 54, SEEK_SET);							//跳过图片头信息54个字节

	for (j = height-1; j >= 0; j--)
	{
		for (i = 0; i < width; i++)
		{	
			memset(tmpbuf, 0, sizeof(tmpbuf));
			read(fd, tmpbuf, sizeof(tmpbuf));
			DrawOnePixel(pFb, x + i, y + j, tmpbuf[2], tmpbuf[1], tmpbuf[0]);
			if(i == width - 1)							//判断最后是否有字节对其补齐的字节
			{
				if(width * 3 % 4 != 0)
				{
					read(fd, tmpbuf, 4 - width * 3 % 4);
				}
			}
		}
	}
	
	close(fd);

	return 0;
}

int SaveMouseInfo(FB_T *pTmpRgbInfo, int x, int y, int Width, int Height, unsigned char (*pTmpBuff)[1024], int Len)
{
	int i = 0;
	int j = 0;

	memset(pTmpBuff, 0, Len * 1024);								//将用于存储备份的空间进行清零	
	if (IS_RGB565_FORMAT(pTmpRgbInfo->ScreenInfo.bits_per_pixel))
	{
		for (j = 0; j < Height; j++)
		{
			for (i = 0; i < Width; i++)
			{
				memcpy(&pTmpBuff[j][i * 2], pTmpRgbInfo->pRGB565Addr + (y + j) * pTmpRgbInfo->ScreenInfo.xres_virtual + (x + i), 2);		//将鼠标即将显示的位置的数据进行复制备份
			}
		}
	}
	else if (IS_RGB888_FORMAT(pTmpRgbInfo->ScreenInfo.bits_per_pixel))
	{
		for (j = 0; j < Height; j++)
		{
			for (i = 0; i < Width; i++)
			{
				memcpy(&pTmpBuff[j][i * 4], pTmpRgbInfo->pRGB888Addr + (y + j) * pTmpRgbInfo->ScreenInfo.xres_virtual + (x + i), 4);
			}
		}
	}

	return 0;
}

int ReloadMouseInfo(FB_T *pTmpRgbInfo, int x, int y, int Width, int Height, unsigned char (*pTmpBuff)[1024], int Len)
{
	int i = 0;
	int j = 0;

	if (IS_RGB565_FORMAT(pTmpRgbInfo->ScreenInfo.bits_per_pixel))
	{
		for (j = 0; j < Height; j++)
		{
			for (i = 0; i < Width; i++)
			{
				memcpy(pTmpRgbInfo->pRGB565Addr + (y + j) * pTmpRgbInfo->ScreenInfo.xres_virtual + (x + i), &pTmpBuff[j][i * 2], 2);	//将备份的数据重新写入回鼠标的位置
			}
		}
	}
	else if (IS_RGB888_FORMAT(pTmpRgbInfo->ScreenInfo.bits_per_pixel))
	{
		for (j = 0; j < Height; j++)
		{
			for (i = 0; i < Width; i++)
			{
				memcpy(pTmpRgbInfo->pRGB888Addr + (y + j) * pTmpRgbInfo->ScreenInfo.xres_virtual + (x + i), &pTmpBuff[j][i * 4], 4);	
			}
		}
	}

	return 0;
}

unsigned char *BmpInit(FB_T *pTmpRgbInfo, char *pBmpName, int Width, int Height)
{
	unsigned char BmpPixelColor[3] = {0};
	unsigned char *pBmpBuff = NULL;
	int FdBmp = 0;
	int i = 0;
	int j = 0;

	FdBmp = open(pBmpName, O_RDONLY);
	if (-1 == FdBmp)
	{
		perror("fail to open");
		return NULL;
	}

	pBmpBuff = malloc(Width * Height * pTmpRgbInfo->ScreenInfo.bits_per_pixel / 8);
	if (NULL == pBmpBuff)
	{
		perror("fail to malloc");
		return NULL;
	}

	lseek(FdBmp, 54, SEEK_SET);

	for (j = Height-1; j >= 0; j--)
	{
		for (i = 0; i < Width; i++)
		{
			memset(BmpPixelColor, 0, sizeof(BmpPixelColor));
			read(FdBmp, BmpPixelColor, sizeof(BmpPixelColor));

			if (IS_RGB888_FORMAT(pTmpRgbInfo->ScreenInfo.bits_per_pixel))
			{
				RGB888_T TmpColor;

				FILL_RGB888_COL(&TmpColor, BmpPixelColor[2], BmpPixelColor[1], BmpPixelColor[0]);
				memcpy((pBmpBuff + (pTmpRgbInfo->ScreenInfo.bits_per_pixel / 8) * (j * pTmpRgbInfo->ScreenInfo.xres_virtual + i)),
				       &TmpColor, sizeof(TmpColor));
			}
			else if (IS_RGB565_FORMAT(pTmpRgbInfo->ScreenInfo.bits_per_pixel))
			{
				RGB565_T TmpColor;

				FILL_RGB565_COL(&TmpColor, BmpPixelColor[2], BmpPixelColor[1], BmpPixelColor[0]);
				memcpy((pBmpBuff + pTmpRgbInfo->ScreenInfo.bits_per_pixel / 8 * (j * pTmpRgbInfo->ScreenInfo.xres_virtual + i)),
				        &TmpColor, sizeof(TmpColor));
			}

			if(i == Width - 1)
			{
				if(Width * 3 % 4 != 0)
				{
					read(FdBmp, BmpPixelColor,4 - Width * 3 % 4);
				}
			}
		}
	}

	close(FdBmp);

	return pBmpBuff;
}

int DrawBmpBuff(FB_T *pFb, unsigned char *pBmpBuff, int width, int height)
{
	if (IS_RGB565_FORMAT(pFb->ScreenInfo.bits_per_pixel))
	{
		memcpy(pFb->pRGB565Addr, pBmpBuff, width * height * pFb->ScreenInfo.bits_per_pixel / 8);
	}
	else if (IS_RGB888_FORMAT(pFb->ScreenInfo.bits_per_pixel))
	{
		memcpy(pFb->pRGB888Addr, pBmpBuff, width * height * pFb->ScreenInfo.bits_per_pixel / 8);
	}

	return 0;
}

int BmpDeInit(unsigned char *pBmpBuff)
{
	free(pBmpBuff);

	return 0;
}
